package io.dubbo.springboot;

import com.alibaba.dubbo.config.*;
import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.dubbo.config.annotation.Service;
import com.alibaba.dubbo.config.spring.ReferenceBean;
import com.alibaba.dubbo.config.spring.ServiceBean;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;

/**
 * 修改一些 注解不能配置的问题
 */
public class AnnotationBeanScanner extends AnnotationBeanEx {


    /**
     * 处理 Reference
     */
    @Override
    protected Object refer(Reference reference, Class<?> referenceClass) {
        String interfaceName;
        ApplicationContext applicationContext = getApplicationContext();
        ConcurrentMap<String, ReferenceBean<?>> referenceConfigs = getReferenceConfigs();
        if (!"".equals(reference.interfaceName())) {
            interfaceName = reference.interfaceName();
        } else if (!void.class.equals(reference.interfaceClass())) {
            interfaceName = reference.interfaceClass().getName();
        } else if (referenceClass.isInterface()) {
            interfaceName = referenceClass.getName();
        } else {
            throw new IllegalStateException("The @Reference undefined interfaceClass or interfaceName, and the property type " +
                referenceClass.getName() + " is not a interface.");
        }
        DubboProperties properties = applicationContext.getBean(DubboProperties.class);
        String group = reference.group();
        String propertiesGroup = properties.getGroup();
        if ("".equals(group) && propertiesGroup != null && !propertiesGroup.trim().equals("")) {
            group = propertiesGroup;
        }
        String version = reference.version();
        String propertiesVersion = properties.getVersion();
        if ("".equals(version) && propertiesVersion != null && !propertiesVersion.trim().equals("")) {
            version = propertiesVersion;
        }
        String key = group + "/" + interfaceName + ":" + version;
        ReferenceBean<?> referenceConfig = referenceConfigs.get(key);
        if (referenceConfig == null) {
            referenceConfig = new ReferenceBean<Object>(reference);
            referenceConfig.setVersion(version);
            referenceConfig.setGroup(group);
            if (void.class.equals(reference.interfaceClass())
                && "".equals(reference.interfaceName())
                && referenceClass.isInterface()) {
                referenceConfig.setInterface(referenceClass);
            }
            if (applicationContext != null) {
                referenceConfig.setApplicationContext(applicationContext);
                // registry
                if (reference.registry().length > 0) {
                    List<RegistryConfig> registryConfigs = new ArrayList<RegistryConfig>();
                    for (String registryId : reference.registry()) {
                        if (registryId != null && registryId.length() > 0) {
                            registryConfigs.add((RegistryConfig) applicationContext.getBean(registryId, RegistryConfig.class));
                        }
                    }
                    referenceConfig.setRegistries(registryConfigs);
                }
                //consumer
                if (reference.consumer().length() > 0) {
                    referenceConfig.setConsumer((ConsumerConfig) applicationContext.getBean(reference.consumer(), ConsumerConfig.class));
                }
                //monitor
                if (reference.monitor().length() > 0) {
                    referenceConfig.setMonitor((MonitorConfig) applicationContext.getBean(reference.monitor(), MonitorConfig.class));
                }
                //application
                if (reference.application().length() > 0) {
                    referenceConfig.setApplication((ApplicationConfig) applicationContext.getBean(reference.application(), ApplicationConfig.class));
                }
                //reference
                if (reference.module().length() > 0) {
                    referenceConfig.setModule((ModuleConfig) applicationContext.getBean(reference.module(), ModuleConfig.class));
                }
                //consumer
                if (reference.consumer().length() > 0) {
                    referenceConfig.setConsumer((ConsumerConfig) applicationContext.getBean(reference.consumer(), ConsumerConfig.class));
                }
                try {
                    referenceConfig.afterPropertiesSet();
                } catch (RuntimeException e) {
                    throw (RuntimeException) e;
                } catch (Exception e) {
                    throw new IllegalStateException(e.getMessage(), e);
                }
            }
            referenceConfigs.putIfAbsent(key, referenceConfig);
            referenceConfig = referenceConfigs.get(key);
        }
        return referenceConfig.get();
    }

    /**
     * 处理 Service
     *
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    public Object postProcessAfterInitialization(Object bean, String beanName)
        throws BeansException {
        if (!isMatchPackage(bean)) {
            return bean;
        }
        Class<?> clazz = bean.getClass();
        if (isProxyBean(bean)) {
            clazz = AopUtils.getTargetClass(bean);
        }
        Service service = clazz.getAnnotation(Service.class);
        if (service == null)
            return bean;
        ApplicationContext applicationContext = getApplicationContext();
        Set<ServiceConfig<?>> serviceConfigs = getServiceConfigs();
        ServiceBean<Object> serviceConfig = new ServiceBean<Object>(service);
        if (void.class.equals(service.interfaceClass())
            && "".equals(service.interfaceName())) {
            if (clazz.getInterfaces().length > 0) {
                serviceConfig.setInterface(clazz.getInterfaces()[0]);
            } else {
                throw new IllegalStateException("Failed to export remote service class " + clazz.getName() +
                    ", cause: The @Service undefined interfaceClass or interfaceName, and the service class unimplemented any interfaces.");
            }
        }
        if (applicationContext == null) {
            return bean;
        }
        serviceConfig.setApplicationContext(applicationContext);
        //都有 default 属性就不可能为 null
        // registry
        if (service.registry().length > 0) {
            List<RegistryConfig> registryConfigs = new ArrayList<RegistryConfig>();
            for (String registryId : service.registry()) {
                if (registryId != null && registryId.length() > 0) {
                    registryConfigs.add((RegistryConfig) applicationContext.getBean(registryId, RegistryConfig.class));
                }
            }
            serviceConfig.setRegistries(registryConfigs);
        }
        //provider
        if (service.provider().length() > 0) {
            serviceConfig.setProvider((ProviderConfig) applicationContext.getBean(service.provider(), ProviderConfig.class));
        }
        // monitor
        if (service.monitor().length() > 0) {
            serviceConfig.setMonitor((MonitorConfig) applicationContext.getBean(service.monitor(), MonitorConfig.class));
        }
        // application
        if (service.application().length() > 0) {
            serviceConfig.setApplication((ApplicationConfig) applicationContext.getBean(service.application(), ApplicationConfig.class));
        }
        // module
        if (service.module().length() > 0) {
            serviceConfig.setModule((ModuleConfig) applicationContext.getBean(service.module(), ModuleConfig.class));
        }
        //provider
        if (service.provider().length() > 0) {
            serviceConfig.setProvider((ProviderConfig) applicationContext.getBean(service.provider(), ProviderConfig.class));
        }
        //protocol
        if (service.protocol().length > 0) {
            List<ProtocolConfig> protocolConfigs = new ArrayList<ProtocolConfig>();
            // modified by lishen; fix dubbo's bug
            for (String protocolId : service.protocol()) {
                if (protocolId != null && protocolId.length() > 0) {
                    protocolConfigs.add((ProtocolConfig) applicationContext.getBean(protocolId, ProtocolConfig.class));
                }
            }
            serviceConfig.setProtocols(protocolConfigs);
        }
        DubboProperties dubboProperties = applicationContext.getBean(DubboProperties.class);
        //超时时间配置
        if (service.timeout() == 0) {
            serviceConfig.setTimeout(dubboProperties.getTimeout());
        }
        //默认版本
        if ("".equals(service.version().trim())) {
            serviceConfig.setVersion(dubboProperties.getVersion());
        }
        String propertiesGroup = dubboProperties.getGroup();
        if ("".equals(service.group()) && propertiesGroup != null && !propertiesGroup.trim().equals("")) {
            serviceConfig.setGroup(propertiesGroup);
        } else {
            serviceConfig.setGroup(service.group());
        }
        try {
            serviceConfig.afterPropertiesSet();
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
        serviceConfig.setRef(bean);
        serviceConfigs.add(serviceConfig);
        serviceConfig.export();
        return bean;
    }

}